package com.atguigu.common.exception;

import com.atguigu.common.results.R;
import com.atguigu.common.results.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

//controller接口的异常处理器
//@Component
//@ControllerAdvice  给异常处理器赋能(该组件才可以处理接口中出现的异常)
//                   它的处理器返回的结果会被当做视图名进行转发
@RestControllerAdvice //处理异常后返回的结果被转为json响应
@Slf4j
public class GlobalExceptionHandler {

    //1、Exception的处理器：最大异常处理器
    @ExceptionHandler(value = Exception.class)
    public R exception(Exception e){
//        log.error(e.getMessage());
        //获取异常的堆栈消息输出
        log.error(ExceptionUtils.getStackTrace(e));
        return R.error();
    }
    //自定义异常处理器
    @ExceptionHandler(value = BusinessException.class)
    public R exception(BusinessException e){
        log.error(e.getMessage());
        return R.error().code(e.getCode()).message(e.getMessage());
    }

    /**
     * Controller上一层相关异常
     */
    @ExceptionHandler({
            NoHandlerFoundException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
            MethodArgumentNotValidException.class,
            HttpMediaTypeNotAcceptableException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            MissingServletRequestPartException.class,
            AsyncRequestTimeoutException.class
    })
    public R handleServletException(Exception e) {
        log.error(e.getMessage(), e);
        //SERVLET_ERROR(-102, "servlet请求异常"),
        return R.error().message(ResultCodeEnum.SERVLET_ERROR.getMessage()).code(ResultCodeEnum.SERVLET_ERROR.getCode());
    }

    /*
        现在异常处理的问题：
            1、我们要为每一个异常创建异常处理器来处理它
            2、因为异常太多，总有异常不能被具体异常处理器处理 最后被Exception的处理器处理
                返回一个模糊的异常响应对象  前端无法定位异常信息
        解决：
            1、只要让抛出的异常类型都一致，只需一个异常处理器即可
            2、为了在异常处理器中能够返回一个携带具体异常信息和状态码的R对象，需要在异常对象中
                携带code和message参数
            现有异常对象不能携带code和其他额外的参数，可以自定义异常类对他进行扩展
            异常类继承运行时异常RuntimeException(Exception为编译时异常，编译不能通过必须处理，运行时
            异常可以被抛出)
     */
    //2、具体异常的处理器：
    // 2.1 ArithmeticException
    @ExceptionHandler(value = ArithmeticException.class)
    public R exception(ArithmeticException e){
//        log.error(e.getMessage());
        //获取异常的堆栈消息输出
        log.error(ExceptionUtils.getStackTrace(e));
        return R.error().code(-1111).message("除数不能为0");
    }
    //2.2 BadSqlGrammarException
    @ExceptionHandler(value = BadSqlGrammarException.class)
    public R exception(BadSqlGrammarException e){
//        log.error(e.getMessage());
        //获取异常的堆栈消息输出
        log.error(ExceptionUtils.getStackTrace(e));
        return R.setResult(ResultCodeEnum.BAD_SQL_GRAMMAR_ERROR);
    }

    // 通过自定义异常优化异常处理，现在每个异常一个处理器，代码冗余
}
